package com.gitee.wsl.ext.array


/**
 * Compares ByteArray to [other] ByteArray.
 * Returns zero if this object is equal to the specified [other] object,
 * a negative number if it's less than [other],
 * or a positive number if it's greater than [other].
 */
infix operator fun ByteArray.compareTo(other: ByteArray): Int {
    val minSize = minOf(this.size, other.size)
    for (it in 0 until minSize) {
        val a = this[it].toUByte()
        val b = other[it].toUByte()
        if (a != b) {
            return a.toInt() - b.toInt()
        }
    }
    return this.size - other.size
}

/**
 * Compares ByteArray to [other] ByteArray.
 * Returns zero if this object is equal to the specified [other] object,
 * a negative number if it's less than [other],
 * or a positive number if it's greater than [other].
 */
fun ByteArray.compareToWithOffsetLength(other: ByteArray, offset: Int, length: Int = other.size - offset): Int {
    val minSize = minOf(this.size, length)
    for (it in 0 until minSize) {
        val a = this[it].toUByte()
        val b = other[it + offset].toUByte()
        if (a != b) {
            return a.toInt() - b.toInt()
        }
    }
    return this.size - length
}

/**
 * Compares only defined bytes of ByteArray to [other] ByteArray.
 * Returns zero if this object is equal to the specified [other] object,
 * a negative number if it's less than [other],
 * or a positive number if it's greater than [other].
 */
fun ByteArray.compareDefinedTo(other: ByteArray, offset: Int = 0, length: Int = other.size - offset): Int {
    val minSize = minOf(this.size, length)
    for (it in 0 until minSize) {
        val a = this[it].toUByte()
        val b = other[it + offset].toUByte()
        if (a != b) {
            return a.toInt() - b.toInt()
        }
    }
    return if (length < this.size) this.size - length else 0
}

/**
 * See https://codahale.com/a-lesson-in-timing-attacks/
 *
 *
 *
 * **Analysis**
 *
 *  * Time Complexity: `O(n)`
 *  * Space Complexity: `O(1)`
 *  * Alters Parameters: `false`
 *
 *
 *
 * @param array        to check for equals
 * @param anotherArray to check against array
 * @return if both arrays have the same length and same length for every index
 */
fun ByteArray.constantTimeEquals(anotherArray: ByteArray?): Boolean {
    if (anotherArray == null || size != anotherArray.size) return false

    var result = 0
    for (i in indices) {
        result = result or (this[i].toInt() xor anotherArray[i].toInt())
    }
    return result == 0
}